<?php
/**
 * Created by PhpStorm.
 * User: johnny
 * Date: 16-12-24
 * Time: 下午4:43
 */

namespace common\models;


use common\models\events\RecordListEvent;
use common\models\UUID;
use common\models\behaviors\UpdateBehavior;
use common\models\record_operator\RecordOperatorBasedOnRules;
use common\models\batch_record_operator\BatchRecordOperator;
use common\models\list_filter_builder\MysqlListFilterBuilder;
use common\models\record_list_builder\RecordListQueryBuilder;
use common\models\record_operator\RecordOperatorInterFace;
use common\widgets\pagination\Pagination;
use yii\base\Exception;
use yii\base\Object;
use yii\db\ActiveRecord;
use Yii;
use yii\base\ModelEvent;

class BaseRecord extends ActiveRecord
{
    const BeforeInsertRecord = 'BeforeInsertRecord';
    const AfterInsertRecord = 'AfterInsertRecord';
    const BeforeUpdateRecord = 'BeforeUpdateRecord';
    const AfterUpdateRecord = 'AfterUpdateRecord';
    const BeforeDeleteRecord = 'BeforeDeleteRecord';
    const AfterDeleteRecord = 'AfterDeleteRecord';

    const Enable = 127;
    const Disable = 126;
    //***************************tableName***************************//
    const User = "user";

    public $pageSize = 100;
    public $enablePageSize = false;

    public $recordOperator;
    public $recordListBuilder;
    public $listFilterBuilder;
    public $batchRecordOperator;

    public function init()
    {
        $this->recordOperator = Yii::$container->get(RecordOperatorBasedOnRules::className());
        $this->recordListBuilder = Yii::$container->get(RecordListQueryBuilder::className());
        $this->listFilterBuilder = Yii::$container->get(MysqlListFilterBuilder::className());
        $this->batchRecordOperator = Yii::$container->get(BatchRecordOperator::className());
    }

    public function behaviors()
    {
        return [
            [
                'class'=>UpdateBehavior::className(),
            ]
        ];
    }

    public function buildRecordListRules() {

    }

    public function listFilterRules($filter) {

    }

    public function insertRecordRules($e) {

    }

    public function updateRecordRules($e) {

    }

    public function formDataPreHandler(&$formData, $record = null) {
        $time = time();
        if(empty($record)) {
            if (!isset($formData['uuid']) || empty($formData['uuid'])) {
                $formData['uuid'] = UUID::getUUID();
            }
            if (!isset($formData['created_time']) || empty($formData['created_time'])) {
                $formData['created_time'] = $time;
            }
            if (!isset($formData['create_time']) || empty($formData['create_time'])) {
                $formData['create_time'] = $time;
            }
            if (!empty(Yii::$app->get('user', false))
                && !Yii::$app->getUser()->isGuest
                && (!isset($formData['created_uuid']) || empty($formData['created_uuid']))) {
                $formData['created_uuid'] = Yii::$app->getUser()->getId();
            }
            $this->clearEmptyField($formData);
        }
        $formData['updated_time'] = $time;
        $formData['update_time'] = $time;
    }

    public function updateRecordBuilder($formData, $record = null) {
        $columns = $this->getTableSchema()->columns;
        $flag = false;
        foreach ($columns as $key => $column) {
            if (isset($formData[$key])) {
                if (!empty($record)) {
                    if($formData[$key] instanceof Object) {
                        $record->$key = $formData[$key];
                    } elseif(is_string($formData[$key]) || is_numeric($formData[$key])) {
                        $record->$key = trim($formData[$key], ' ');
                    } elseif(is_callable($formData[$key])) {
                        $record->$key = call_user_func($formData[$key], $formData);
                    }
                } else {
                    if($formData[$key] instanceof Object) {
                        $this->$key = $formData[$key];
                    } elseif (is_string($formData[$key]) || is_numeric($formData[$key])) {
                        $this->$key = trim($formData[$key], ' ');
                    } elseif(is_callable($formData[$key])) {
                        $this->$key = call_user_func($formData[$key], $formData);
                    }
                }
                $flag = true;
            }
        }
        return $flag;
    }

    /**
     * 在更新或是插入数据之前对自己的一些处理
     * @param $formData
     * @param null $record
     */
    public function recordPreHandler(&$formData, $record = null) {

    }

    /**
     * 将formData里面的空的地方删除掉,提升效率
     * @param $formData
     */
    public function clearEmptyField(&$formData) {
        foreach($formData as $index => $value) {
            if($formData[$index] == '') {
                unset($formData[$index]);
            }
            else if(is_string($formData[$index])) {
                $formData[$index] = trim($formData[$index]);
            }
        }
    }

    /**
     *
     * @param $e
     * @return bool
     */
    public function beforeInsertRecord($e) {
        $this->trigger(self::BeforeInsertRecord, $e);
        $this->formDataPreHandler($e->formData);

        if (!$this->updateRecordBuilder($e->formData)) {
            $e->isValid = false;
            return false;
        }

        $this->recordPreHandler($e->formData);

        return true;
    }

    public function afterInsertRecord($e) {
        $this->trigger(self::AfterInsertRecord, $e);
        $this->off(self::AfterInsertRecord);
        return $e->isValid;
    }

    const AfterUpdatePreHandler = 'AfterUpdatePreHandler';
    /**
     * @param $e
     * @return bool
     */
    public function beforeUpdateRecord($e) {
        $this->trigger(self::BeforeUpdateRecord, $e);
        $this->formDataPreHandler($e->formData, $e->record);

        if (!$this->updateRecordBuilder($e->formData, $e->record)) {
            $e->isValid = false;
            return false;
        }

        $this->recordPreHandler($e->formData, $e->record);
        $event = new ModelEvent();
        $e->record->trigger(static::AfterUpdatePreHandler, $event);

        return true;
    }

    public function afterUpdateRecord($e) {
        $this->trigger(self::AfterUpdateRecord, $e);
        return $e->isValid;
    }

    public function insertRecord($formData) {
        if(empty($formData)) {
            return true;
        }

        $this->recordOperator->on(RecordOperatorInterFace::BeforeInsertRecord,[$this, 'beforeInsertRecord']);
        $this->recordOperator->on(RecordOperatorInterFace::AfterInsertRecord,[$this, 'afterInsertRecord']);
        return $this->recordOperator->insertRecord($this, $formData);
    }

    public function updateRecord($formData, $condition = null) {
        $condition = empty($condition)?(isset($formData['uuid'])?[
            'uuid'=>$formData['uuid']
        ]:[
            'id'=>$formData['id']
        ]):$condition;
        $record = self::find()->where($condition)->one();
        if (empty($record)) {
            return true;
        }
        $record->setScenario($this->getScenario());
        $this->recordOperator->on(RecordOperatorInterFace::BeforeUpdateRecord,[$this, 'beforeUpdateRecord']);
        $this->recordOperator->on(RecordOperatorInterFace::AfterUpdateRecord,[$this, 'afterUpdateRecord']);

        return $this->recordOperator->updateRecord($record, $formData);
    }

    public function recordList($selects, $condition, $fetchOne = false, $orderBy = null, $enablePage = true, $paginationRoute = null) {
        try {
            $query = $this->recordListBuilder->recordListBuilder($this, $selects, $condition);
            $e = new RecordListEvent([
                'query'=>$query,
                'orderBy'=>empty($orderBy)?[
                    't1.id' => SORT_DESC,
                ]:$orderBy,
                'recordList'=>[],
                'enablePage'=>$enablePage,
            ]);
            $this->trigger(RecordListEvent::BeforeRecordList, $e);
        } catch (Exception $e) {
            throw $e;
        }

        if ($fetchOne) {
            $record = $query->asArray()->one();
            return $record;
        }

        if (!$e->enablePage) {
            if($this->enablePageSize) {
                return $query->orderBy($e->orderBy)->limit($this->pageSize)->asArray()->all();
            }
            return $query->orderBy($e->orderBy)->asArray()->all();
        }

        $pagination = new Pagination([
            'totalCount'=>$query->count(),
            'pageSize' => $this->pageSize,
            'route'=>$paginationRoute,
        ]);

        try {
            $list = $query->orderBy($e->orderBy)->offset($pagination->offset)->limit($pagination->limit)->asArray()->all();
        } catch (\Exception $e) {
            throw $e;
        }
        $e->recordList['pagination'] = $pagination;
        $e->recordList['list'] = $list;
        $this->trigger(RecordListEvent::AfterRecordList, $e);

        return $e->recordList;
    }

    public function listFilter($selects, $filter, $initial_condition = null, $enablePage = true) {
        if (!$enablePage) {
            $this->on(RecordListEvent::BeforeRecordList, function ($e) {
                $e->enablePage = false;
            });
        }
        if (empty($filter)) {
            return $this->recordList($selects, $initial_condition, false, null);
        }

        return $this->listFilterBuilder->listFilterBuilder($this, $filter, $initial_condition, $selects);
    }

    public function getRecord($selects, $condition) {
        return $this->recordList($selects, $condition, true);
    }

    public function beforeDeleteRecord($e) {
        $this->trigger(self::BeforeDeleteRecord, $e);
        return $e->isValid;
    }

    public function afterDeleteRecord($e) {
        $this->trigger(self::AfterDeleteRecord, $e);
        return $e->isValid;
    }

    public function deleteRecordRules($e) {
        return null;
    }

    public function deleteRecord($condition) {
        if(empty($condition)) {
            return true;
        }

        $record = static::find()->where($condition)->one();
        if(empty($record)) {
            return true;
        }

        $record->setScenario($this->getScenario());
        $this->recordOperator->on(RecordOperatorInterFace::BeforeDeleteRecord,[$this, 'beforeDeleteRecord']);
        $this->recordOperator->on(RecordOperatorInterFace::AfterDeleteRecord,[$this, 'afterDeleteRecord']);
        return $this->recordOperator->deleteRecord($record);
    }

    public function beforeBatchInsertRecord($e) {
        $operator = $e->sender;
        $operator->table = static::tableName();
        return $e->isValid;
    }

    public function afterBatchInsertRecord($e) {

    }

    public function batchInsertRecordRules($e) {

    }

    public function batchInsertRecord($formData) {
        if(empty($formData)) {
            return true;
        }

        $this->batchRecordOperator->on(BatchRecordOperator::BeforeBatchInsertRecord,[$this, 'beforeBatchInsertRecord']);
        $this->batchRecordOperator->on(BatchRecordOperator::AfterBatchInsertRecord,[$this, 'afterBatchInsertRecord']);
        return $this->batchRecordOperator->batchInsertRecord($this, $formData);
    }

    public function beforeBatchUpdateRecord($e) {
        $operator = $e->sender;
        $operator->table = static::tableName();
        return $e->isValid;
    }

    public function afterBatchUpdateRecord($e) {

    }

    public function batchUpdateRecordRules($e) {

    }

    public function batchUpdateRecord($formData) {
        if(empty($formData)) {
            return true;
        }

        $this->batchRecordOperator->on(BatchRecordOperator::BeforeBatchUpdateRecord,[$this, 'beforeBatchUpdateRecord']);
        $this->batchRecordOperator->on(BatchRecordOperator::AfterBatchUpdateRecord,[$this, 'afterBatchUpdateRecord']);
        return $this->batchRecordOperator->batchUpdateRecord($this, $formData);
    }

    public function beforeBatchDeleteRecord($e) {
        $operator = $e->sender;
        $operator->table = static::tableName();
        return $e->isValid;
    }

    public function afterBatchDeleteRecord($e) {

    }

    public function batchDeleteRecordRules($e) {

    }

    public function batchDeleteRecord($formData) {
        if(empty($formData)) {
            return true;
        }

        $this->batchRecordOperator->on(BatchRecordOperator::BeforeBatchDeleteRecord,[$this, 'beforeBatchDeleteRecord']);
        $this->batchRecordOperator->on(BatchRecordOperator::AfterBatchDeleteRecord,[$this, 'afterBatchDeleteRecord']);
        return $this->batchRecordOperator->batchDeleteRecord($this, $formData);
    }

    /**
     * 将formdata 的时间字符串变成时间戳
     * @param $formData
     * @param $index
     * @return false|int|null
     */
    public function formDataStrToTime($formData, $index) {
        if (!isset($formData[$index]) || empty($formData[$index])) {
            return null;
        }

        if (is_numeric($formData[$index])) {
            return $formData[$index];
        }

        return strtotime($formData[$index]);
    }

    // 获得指定field的列表
    public function getAppointedFieldList($recordList, $field) {
        if (empty($recordList)) {
            return null;
        }

        $_list = null;
        foreach ($recordList as $record) {
            $_list[] = $record[$field];
        }

        return $_list;
    }

    /**
     * 从数据库里面查出来的结果集里面构成键值对
     * @param $recordList
     * @param $key
     * @param $value
     * @return mixed
     */
    public function buildKVP($recordList, $key, $value) {
        if (empty($recordList)) {
            return null;
        }

        $_list = null;
        foreach ($recordList as $record) {
            if (!isset($record[$key])) {
                continue;
            }

            $_list[$record[$key]] = $record[$value];
        }

        return $_list;
    }

    // 将form里面输入的时间字符串转换成时间戳，
    // 如果输入为空的话，将这个字段unset掉
    public function handlerFormDataTime(&$formData, $index) {
        if(isset($formData[$index]) && !empty($formData[$index])) {
            $formData[$index] = strtotime($formData[$index]);
            return $formData[$index];
        } elseif(empty($formData[$index])) {
            unset($formData[$index]);
        }
    }
}